﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using Ionic.Zip;
using System.Diagnostics;
using System.Net;
using EyFramework.AutoUpdate;
using EyFramework.AutoUpdate.Entity;
using System.Web.Script.Serialization;

namespace EyFramework.AutoUpdate
{
    public partial class FrmUpdate : Form
    {
        //无窗体拖拽
        [DllImport("user32.dll")]//拖动无窗体的控件
        public static extern bool ReleaseCapture();

        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

        public const int WM_SYSCOMMAND = 0x0112;
        public const int SC_MOVE = 0xF010;
        public const int HTCAPTION = 0x0002;
        private List<VersionInfo> versionLists;
        private IniFileUtil iniFile;
        private Entity.StartArgs startArgs;

        public delegate void UpdateLabel(string pUrl);

        public FrmUpdate()
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
        }

        public FrmUpdate(Entity.StartArgs _startArgs)
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
            startArgs = _startArgs;
        }

        private void FrmUpdate_Load(object sender, EventArgs e)
        {
            //结束原进程
            if (!string.IsNullOrEmpty(startArgs.ProcessName))
            {
                Process[] cProcesses = Process.GetProcessesByName(startArgs.ProcessName);
                foreach (Process proc in cProcesses)
                {
                    proc.Kill();
                }
            }
            versionLists = new List<VersionInfo>();
            iniFile = new IniFileUtil(Path.Combine(DirectoryUtil.GetCurrentDirectory(), "AutoUpdate.ini"));
            versionLists = GetUpdateVersions();
            //判断自动更新文件UpdateSetting.xml是否存在，如果存在，解析xml文件
            if (versionLists.Count > 0)
            {
                bw_update.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("未找到需要更新的文件,请确认是否已经是最新版本！");
                this.Close();
            }
        }

        public List<VersionInfo> GetUpdateVersions()
        {
            //获取当前版本
            var currentVersion = GetCurrentVersion();
            //循环获取需要更新的版本列表
            currentVersion.Update.ForEach(p =>
            {
                if (IsNewVersion(p.Version, PublicGlobal.CurrentVersion))
                {
                    versionLists.Add(p);
                }
            });
            return versionLists;
        }

        /// <summary>
        /// 比较第一个版本是否比第二个版本新
        /// 如果是返回true 否则返回false
        /// </summary>
        /// <param name="version1">第一个版本</param>
        /// <param name="version2">第二个版本</param>
        /// <returns></returns>
        public bool IsNewVersion(string pNewVersion, string pOldVersion)
        {
            SysVersion oldV = new SysVersion(pOldVersion);
            SysVersion newV = new SysVersion(pNewVersion);
            if (newV.MajorVersionNumber > oldV.MajorVersionNumber)
            {
                return true;
            }
            else if (newV.MajorVersionNumber == oldV.MajorVersionNumber)
            {
                if (newV.MinorVersionNumber > oldV.MinorVersionNumber)
                {
                    return true;
                }
                else if (newV.MinorVersionNumber == oldV.MinorVersionNumber)
                {
                    if (newV.BuildNumber > oldV.BuildNumber)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public VersionConfig GetCurrentVersion()
        {
            if (!string.IsNullOrEmpty(PublicGlobal.UpdateUrl))
            {
                string retString = string.Empty;
                //发起HTTP请求，获取当前服务器上的最新版本文件
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(PublicGlobal.UpdateUrl);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream myResponseStream = response.GetResponseStream();
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
                retString = myStreamReader.ReadToEnd();
                myStreamReader.Close();
                myResponseStream.Close();
                if (response != null)
                {
                    response.Close();
                }

                if (request != null)
                {
                    request.Abort();
                }
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                VersionConfig uv = serializer.Deserialize<VersionConfig>(retString);
                return uv;
            }
            else
            {
                throw new Exception("未配置自动更新地址！");
            }
        }

        private void panelTitle_MouseDown(object sender, MouseEventArgs e)
        {
            ReleaseCapture();
            SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
        }

        private void pbClose_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("您确定取消更新下载？", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                //停止下载，并删除已经创建的临时文件
                if (bw_update.IsBusy)
                {
                    bw_update.CancelAsync();
                }
                this.Close();
            }
        }

        private void bw_update_DoWork(object sender, DoWorkEventArgs e)
        {
            WriteLog("开始自动更新");
            foreach (VersionInfo us in versionLists)
            {
                UpdateVersion(us); ;
            }
        }

        /// <summary>
        /// 更新指定的文件
        /// </summary>
        /// <param name="updateSetting"></param>
        private void UpdateVersion(VersionInfo versionInfo)
        {
            string downFile = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\update.zip";
            WriteLog("开始下载:" + versionInfo.File);
            //1、开始下载文件
            DownloadFile(versionInfo.File, downFile);
            WriteLog("下载完毕,开始解压程序主目录覆盖，" + downFile);
            //解压到解压到插件目录
            DirectoryInfo info = new DirectoryInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string targetDir = Path.GetDirectoryName(info.FullName);
            UnZipFile(targetDir, downFile);
            //更新版本号
            iniFile.IniWriteValue("AutoUpdate", "AppVersion", versionInfo.Version);
            iniFile.IniWriteValue("AutoUpdate", "UpdateUrl", PublicGlobal.UpdateUrl);
            PublicGlobal.CurrentVersion = versionInfo.Version;
            WriteLog("删除下载的临时文件");
            File.Delete(downFile);
        }

        private void bw_update_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.progressBar1.Value = e.ProgressPercentage;
            lblProcess.Text = e.ProgressPercentage + "%";
        }

        private void bw_update_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                WriteLog("自动更新失败:" + e.Error.ToString());
                MessageBox.Show(e.Error.ToString());
                return;
            }
            if (!e.Cancelled)
            {
                WriteLog("自动更新完毕");
                this.DialogResult = DialogResult.OK;//表示更新完毕了
                StartCmd();
                this.Close();
            }
            else
            {
                WriteLog("自动更新取消");
            }
        }

        private void StartCmd()
        {
            if (!string.IsNullOrEmpty(startArgs.StartCmd))
            {
                WriteLog("启动应用程序:" + startArgs.StartCmd);
                //直接启动更新程序更新，并关闭当前程序
                Process.Start(startArgs.StartCmd);
            }
            else if (!string.IsNullOrEmpty(startArgs.ProcessName))
            {
                //默认启动原进程
                Process.Start(Path.Combine(DirectoryUtil.GetCurrentDirectory(), startArgs.ProcessName));
            }
            Application.Exit();
        }

        private void UpdateUrl(string pUrl)
        {
            this.lbl_url.Text = pUrl;
        }

        private void DownloadFile(string pFileUrl, string pDownFile)
        {
            if (lbl_url.InvokeRequired)
            {
                this.Invoke(new FrmUpdate.UpdateLabel(UpdateUrl), pFileUrl);
            }
            if (File.Exists(pDownFile))
            {
                File.Delete(pDownFile);
            }
            int percent = 0;
            System.IO.Stream st = null;
            System.IO.Stream so = null;
            try
            {
                System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(pFileUrl);
                System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse();
                long totalBytes = myrp.ContentLength;
                st = myrp.GetResponseStream();
                so = new System.IO.FileStream(pDownFile, System.IO.FileMode.Create);
                long totalDownloadedByte = 0;
                byte[] by = new byte[1024];
                int osize = st.Read(by, 0, (int)by.Length);
                while (osize > 0)
                {
                    totalDownloadedByte = osize + totalDownloadedByte;
                    System.Windows.Forms.Application.DoEvents();
                    so.Write(by, 0, osize);
                    osize = st.Read(by, 0, (int)by.Length);

                    percent = Convert.ToInt32(Math.Round((totalDownloadedByte * 1.0 / totalBytes), 2) * 100);
                    bw_update.ReportProgress(percent);
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
                MessageBox.Show("文件下载失败:" + ex.ToString());
            }
            finally
            {
                so.Close();
                st.Close();
            }
        }

        public void WriteLog(string strLog)
        {
            FileStream fs = null;
            StreamWriter sw = null;
            try
            {
                string sFileName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\updateinfo\\" + DateTime.Now.ToString("yyyyMMdd") + ".log";
                string sDirName = Path.GetDirectoryName(sFileName);
                if (!DirectoryUtil.IsExistDirectory(sDirName))
                {
                    DirectoryUtil.CreateDirectory(sDirName);
                }
                if (File.Exists(sFileName)) //验证文件是否存在，有则追加，无则创建
                {
                    fs = new FileStream(sFileName, FileMode.Append, FileAccess.Write);
                }
                else
                {
                    fs = new FileStream(sFileName, FileMode.Create, FileAccess.Write);
                }
                sw = new StreamWriter(fs);
                sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "->" + strLog);
            }
            catch (Exception ex)
            {
                MessageBox.Show("记录调试日志失败:" + ex.ToString());
            }
            finally
            {
                sw.Close();
                fs.Close();
            }
        }

        /// <summary>
        /// 解压zip文件
        /// </summary>
        /// <param name="targetDirectory">解压后目录</param>
        /// <param name="zipFileName">压缩包文件名</param>
        /// <returns>解压结果是否成功</returns>
        public bool UnZipFile(string targetDirectory, string zipFileName)
        {
            bool result = true;
            try
            {
                using (ZipFile zip = new ZipFile(zipFileName, System.Text.Encoding.Default))
                {
                    zip.ExtractAll(targetDirectory, ExtractExistingFileAction.OverwriteSilently);
                }
            }
            catch (Exception)
            {
                result = false;
            }

            return result;
        }
    }
}